﻿#if false
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Util.IO
{
    
#if false
    class MifDeltaEncoding
    {
        public static IEnumerable<IndexRange> FindUnchangedRanges(
            byte[] previous, byte[] current, int threshold, int alignment)
        {
            yield break;
        }
        public static void SaveDeltaEncodedStream(
            byte[] previous, byte[] current, int threshold, int alignment,
            BinaryWriter writer)
        {
            int lastIndex = 0;
            foreach (IndexRange r in FindUnchangedRanges(previous, current, threshold, alignment))
            {
                if (lastIndex == 0)
                {
                    writer.Write(0);
                    writer.Write(r.BeginIndex - lastIndex);
                    writer.Write(current, r.BeginIndex, r.EndIndex - r.BeginIndex);
                }
            }
        }
        /*
        public static IEnumerable<IndexRange> Decode<T>(T[] data)
        {
            yield break;
        }
         * */
    }
#endif

#if false
    public class DeltaChunk { }

    public class DeltaPatch<T>
    {
        public List<int> chunkSizes;
        public List<T> delta;
    }
#endif

    public struct IndexRange
    {
        private int beginIndex;
        private int endIndex;

        public int BeginIndex { get { return beginIndex; } }
        public int EndIndex { get { return endIndex; } }
        public int Length { get { return endIndex - beginIndex; } }

        public IndexRange(int beginIndex, int endIndex)
        {
            if (beginIndex < 0)
                throw new ArgumentOutOfRangeException("beginIndex");
            if (endIndex < beginIndex)
                throw new ArgumentOutOfRangeException("endIndex");

            this.beginIndex = beginIndex;
            this.endIndex = endIndex;
        }
    }

    public enum DeltaRangeType
    {
        Common = 0,
        Distinct = 1
    }

    public class DeltaEncoder
    {
        public static void Encode<T>(
            T[] oldValue, int oldIndex,
            T[] newValue, int newIndex,
            int count, int commonThreshold,
            OutputStream<T> dataOutput, OutputStream<int> controlOutput,
            DeltaRangeType firstRange)
        {
            bool isDistinctRange = (firstRange == DeltaRangeType.Distinct);
            foreach (IndexRange r in GetAlternatingRanges(
                oldValue, oldIndex, newValue, newIndex,
                count, commonThreshold, firstRange))
            {
                controlOutput.Write(r.Length);
                if (isDistinctRange)
                {
                    dataOutput.Write(newValue, newIndex + r.BeginIndex, r.Length);
                }
                isDistinctRange = !isDistinctRange;
            }
        }

        /// <summary>
        /// Finds the common ranges between two arrays of the same length.
        /// </summary>
        /// <typeparam name="T">Type of the elements contained in the arrays.
        /// The default equality comparer is used to compare whether two 
        /// elements of type T are equal.</typeparam>
        /// <param name="array1">First array.</param>
        /// <param name="offset1">Index of the first element to compare in
        /// the first array.</param>
        /// <param name="array2">Second array.</param>
        /// <param name="offset2">Index of the first element to compare in
        /// the second array.</param>
        /// <param name="count">Number of elements to compare.</param>
        /// <param name="threshold">Only return a common range if its length
        /// is greater than threshold. If threshold is zero, all common ranges
        /// are returned.</param>
        /// <returns>
        /// All maximal common chunks with a length greater than threshold.
        /// </returns>
        /// <remarks>
        /// Let [i,j) be a locally maximal chunk of elements such that
        /// array1[offset1+i..offset1+j-1] = array2[offset2+i..offset2+j-1]
        /// but extending either i or j by one will cause them to be unequal.
        /// The 'threshold' argument should be chosen to be a value indicating
        /// the overhead of encoding a common chunk.
        /// </remarks>
        public static IEnumerable<IndexRange> GetCommonRanges<T>(
            T[] array1, int offset1,
            T[] array2, int offset2,
            int count, int threshold)
        {
            if (array1 == null || array2 == null)
                throw new ArgumentNullException();
            if (offset1 < 0 || offset2 < 0 || count < 0)
                throw new ArgumentOutOfRangeException();
            if (offset1 + count > array1.Length || offset2 + count > array2.Length)
                throw new ArgumentOutOfRangeException();

            for (int index = 0; index < count; )
            {
                // Count length of common chunk.
                int n = GetEqualRunLength(
                    array1, offset1 + index,
                    array2, offset2 + index,
                    count - index);

                // Return common chunk if its length > threshold.
                if (n > threshold)
                    yield return new IndexRange(index, index + n);

                index += n;
                index += GetUnequalRunLength(
                    array1, offset1 + index,
                    array2, offset2 + index,
                    count - index);
            }
        }

        public static IEnumerable<IndexRange> GetDistinctRanges<T>(
            T[] array1, int offset1,
            T[] array2, int offset2,
            int count, int commonThreshold)
        {
            int lastCommonEnd = 0; // end-index of previous common block

            foreach (IndexRange r in GetCommonRanges(
                array1, offset1, array2, offset2, count, commonThreshold))
            {
                // Return the distinct elements in between if any.
                if (r.BeginIndex > lastCommonEnd) 
                    yield return new IndexRange(lastCommonEnd, r.BeginIndex);

                lastCommonEnd = r.EndIndex;
            }

            // Any distinct elements at the end?
            if (count > lastCommonEnd)
                yield return new IndexRange(lastCommonEnd, count);
        }

        public static IEnumerable<IndexRange> GetAlternatingRanges<T>(
            T[] array1, int offset1,
            T[] array2, int offset2,
            int count, int commonThreshold, DeltaRangeType firstRange)
        {
            int lastCommonEnd = 0; // end-index of previous common block

            foreach (IndexRange r in GetCommonRanges(
                array1, offset1, array2, offset2, count, commonThreshold))
            {
                // If the first element is not part of a common range and
                // the caller requests that a common range comes first,
                // yield return an empty common range.
                if (lastCommonEnd == 0 && r.BeginIndex > 0 &&
                    firstRange == DeltaRangeType.Common)
                {
                    yield return new IndexRange(0, 0);
                }

                // Return the distinct elements in between if any.
                if (r.BeginIndex > lastCommonEnd)
                    yield return new IndexRange(lastCommonEnd, r.BeginIndex);

                // Return common block.
                yield return r;

                lastCommonEnd = r.EndIndex;
            }

            // Any distinct elements at the end?
            if (count > lastCommonEnd)
            {
                // If the first element is not part of a common range and
                // the caller requests that a common range comes first,
                // yield return an empty common range.
                if (lastCommonEnd == 0 && firstRange == DeltaRangeType.Common)
                {
                    yield return new IndexRange(0, 0);
                }
                yield return new IndexRange(lastCommonEnd, count);
            }
        }

        /// <summary>
        /// Gets the number of consecutive equal elements starting from the
        /// given locations of two arrays.
        /// </summary>
        /// <param name="array1">First array.</param>
        /// <param name="index1">Index of the first element to compare in the 
        /// first array.</param>
        /// <param name="array2">Second array.</param>
        /// <param name="index2">Index of the first element to compare in the 
        /// second array.</param>
        /// <param name="count">Maximum number of elements to compare.</param>
        /// <returns>
        /// Non-negative integer k such that 
        /// array1[index1..index1+k-1] = array2[index2..index2+k-1] but
        /// array1[index1+k] != array2[index2+k].
        /// </returns>
        public static int GetEqualRunLength<T>(
            T[] array1, int index1, T[] array2, int index2, int count)
        {
            if (array1 == null || array2 == null)
                throw new ArgumentNullException();
            if (index1 < 0 || index2 < 0 || count < 0)
                throw new ArgumentOutOfRangeException();
            if (index1 + count > array1.Length || index2 + count > array2.Length)
                throw new ArgumentOutOfRangeException();

            EqualityComparer<T> cmp = EqualityComparer<T>.Default;
            int k = 0;
            while (k < count && cmp.Equals(array1[index1 + k], array2[index2 + k]))
                k++;
            return k;
        }

        public static int GetUnequalRunLength<T>(
            T[] array1, int index1, T[] array2, int index2, int count)
        {
            if (array1 == null || array2 == null)
                throw new ArgumentNullException();
            if (index1 < 0 || index2 < 0 || count < 0)
                throw new ArgumentOutOfRangeException();
            if (index1 + count > array1.Length || index2 + count > array2.Length)
                throw new ArgumentOutOfRangeException();

            EqualityComparer<T> cmp = EqualityComparer<T>.Default;
            int k = 0;
            while (k < count && !cmp.Equals(array1[index1 + k], array2[index2 + k]))
                k++;
            return k;
        }
    }

#if false
    public class DeltaEncoding<T>
    {
        private DeltaFunction<T> fp; // function pointer

        public void Encode(
            T[] oldValue, int oldIndex,
            T[] newValue, int newIndex,
           int count,             int threshold,
                        T[] delta, int deltaIndex)

        {
            if (newValue == null)
                throw new ArgumentNullException("newValue");
            if (oldValue == null)
                throw new ArgumentNullException("oldValue");
            if (newValue.Length != oldValue.Length)
                throw new ArgumentException("Two arrays must have the same size.");

            // Let (i,j) be a locally maximal chunk of elements such that 
            // previous[i..j] = current[i..j] but extending either i or j by
            // one will cause them to be unequal. Suppose it takes 'threshold'
            // number of elements of overhead to encode a common chunk, then 
            // it makes sense to create a common chunk only if the common 
            // length n = (j - i + 1) > threshold.

            List<int> chunkSizes = new List<int>();
            List<T> delta = new List<T>();

            int lastIndex = 0; // end-offset of the previous common block
            int index = 0;     // begin-offset of the current common block
            while (index < count)
            {
                // Count length of common chunk.
                int n = GetEqualRunLength(
                    oldValue, oldIndex + index,
                    newValue, newIndex + index,
                    count - index);

                if (index == 0 || n > threshold) // makes sense to encode a common chunk
                {
                    // Encode last distinct chunk.
                    if (index > lastIndex)
                    {
                        chunkSizes.Add(index - lastIndex);
                        delta.AddRange(newValue.
                        //diff.Add(CreateChunk(oldValue, newValue, lastIndex, index, type));
                    }
                    // Encode current common chunk.
                    chunkSizes.Add(n);
                    lastIndex = index + n;
                }
                index += n;
                index += GetUnequalRunLength(
                    oldValue, oldIndex + index,
                    newValue, newIndex + index,
                    count - index);
            }

            // Encode last distinct chunk.
            if (index > lastIndex)
            {
                chunkSizes.Add(index - lastIndex);
                //delta.AddRange(new
                //diff.Add(CreateChunk(oldValue, newValue, lastIndex, index, type));
            }

            // Create an DeltaPatch object.
            this.type = type;
            this.changes = diff.ToArray();
        }
#endif

#if false
        public void Encode(
            T[] oldValue, int oldIndex,
            T[] newValue, int newIndex,
           int count,             int threshold,
                        T[] delta, int deltaIndex)

        {
            if (newValue == null)
                throw new ArgumentNullException("newValue");
            if (oldValue == null)
                throw new ArgumentNullException("oldValue");
            if (newValue.Length != oldValue.Length)
                throw new ArgumentException("Two arrays must have the same size.");

            // Let (i,j) be a locally maximal chunk of elements such that 
            // previous[i..j] = current[i..j] but extending either i or j by
            // one will cause them to be unequal. Suppose it takes 'threshold'
            // number of elements of overhead to encode a common chunk, then 
            // it makes sense to create a common chunk only if the common 
            // length n = (j - i + 1) > threshold.

            List<int> chunkSizes = new List<int>();
            List<T> delta = new List<T>();

            int lastIndex = 0; // end-offset of the previous common block
            int index = 0;     // begin-offset of the current common block
            while (index < count)
            {
                // Count length of common chunk.
                int n = GetEqualRunLength(
                    oldValue, oldIndex + index,
                    newValue, newIndex + index,
                    count - index);

                if (index == 0 || n > threshold) // makes sense to encode a common chunk
                {
                    // Encode last distinct chunk.
                    if (index > lastIndex)
                    {
                        chunkSizes.Add(index - lastIndex);
                        delta.AddRange(newValue.
                        //diff.Add(CreateChunk(oldValue, newValue, lastIndex, index, type));
                    }
                    // Encode current common chunk.
                    chunkSizes.Add(n);
                    lastIndex = index + n;
                }
                index += n;
                index += GetUnequalRunLength(
                    oldValue, oldIndex + index,
                    newValue, newIndex + index,
                    count - index);
            }

            // Encode last distinct chunk.
            if (index > lastIndex)
            {
                chunkSizes.Add(index - lastIndex);
                //delta.AddRange(new
                //diff.Add(CreateChunk(oldValue, newValue, lastIndex, index, type));
            }

            // Create an DeltaPatch object.
            this.type = type;
            this.changes = diff.ToArray();
        }
    }
#endif

    public abstract class DeltaFunction<T>
    {
        public abstract void GetDelta(
            T[] oldValue, int oldIndex,
            T[] newValue, int newIndex,
            T[] delta, int deltaIndex, int count);
        public abstract void GetNewValue(
            T[] oldValue, int oldIndex,
            T[] delta, int deltaIndex,
            T[] newValue, int newIndex, int count);
        public abstract void GetOldValue(
            T[] newValue, int newIndex,
            T[] delta, int deltaIndex,
            T[] oldValue, int oldIndex, int count);

        public static DeltaFunction<T> TakeNewValue
        {
            get { return new TakeNewValueFunction<T>(); }
        }
    }

    class TakeNewValueFunction<T> : DeltaFunction<T>
    {
        public override void GetDelta(
            T[] oldValue, int oldIndex, T[] newValue, int newIndex,
            T[] delta, int deltaIndex, int count)
        {
            Array.Copy(newValue, newIndex, delta, deltaIndex, count);
        }

        public override void GetNewValue(
            T[] oldValue, int oldIndex, T[] delta, int deltaIndex,
            T[] newValue, int newIndex, int count)
        {
            Array.Copy(delta, deltaIndex, newValue, newIndex, count);
        }

        public override void GetOldValue(
            T[] newValue, int newIndex, T[] delta, int deltaIndex,
            T[] oldValue, int oldIndex, int count)
        {
            throw new NotSupportedException("TakeNewValueFunction does not support GetOldValue().");
        }
    }

#if false
    class ByteXorFunction : DeltaFunction<byte>
    {
        public override void GetDelta(
            byte[] oldValue, int oldIndex, byte[] newValue, int newIndex, 
            byte[] delta, int deltaIndex, int count)
        {
            for (int i = 0; i < count; i++)
            {
                delta[deltaIndex + i] = (byte)(oldValue[oldIndex + i] ^ newValue[newIndex + i]);
            }
        }

        public override void GetNewValue(
            byte[] oldValue, int oldIndex, byte[] delta, int deltaIndex,
            byte[] newValue, int newIndex, int count)
        {
            GetDelta(oldValue, oldIndex, delta, deltaIndex, newValue, newIndex, count);
        }

        public override void GetOldValue(
            byte[] newValue, int newIndex, byte[] delta, int deltaIndex, 
            byte[] oldValue, int oldIndex, int count)
        {
            GetDelta(newValue, newIndex, delta, deltaIndex, oldValue, oldIndex, count);
        }
    }
#endif
}
#endif
